package org.dtrd.modules.record.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.dtrd.common.util.*;
import org.dtrd.config.system.Constant;
import org.dtrd.config.system.Constant.RecordType;
import org.dtrd.config.system.Constant.SugarRecordType;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninRecordService;
import org.dtrd.modules.device.entity.bean.bioland.SrcDataValue;
import org.dtrd.modules.device.entity.bean.xty.XtySugarRecord;
import org.dtrd.modules.device.util.SGBLBLEParseUtil;
import org.dtrd.modules.doctor.entity.bean.DoctorIndexDynamicInfo;
import org.dtrd.modules.doctor.entity.bean.PieChartItem;
import org.dtrd.modules.doctor.service.IDtrdRlDoctorPatientService;
import org.dtrd.modules.message.util.MsgEventPusher;
import org.dtrd.modules.patient.service.IDtrdEntRdPatientService;
import org.dtrd.modules.record.entity.bean.*;
import org.dtrd.modules.record.entity.po.DtrdEntRdSigninSugarValue;
import org.dtrd.modules.record.entity.po.DtrdViewRlPatientSugarValue;
import org.dtrd.modules.record.mapper.DtrdEntRdSugarValueMapper;
import org.dtrd.modules.record.service.IDtrdEntRdUplowRecordService;
import org.dtrd.modules.record.service.IDtrdViewRlCheckinRecordService;
import org.dtrd.modules.record.service.IDtrdViewRlPatientSugarValueService;
import org.dtrd.modules.record.service.ISugarRecordService;
import org.dtrd.modules.record.util.SugarRecordUtil;
import org.dtrd.modules.util.SchemeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author 萧
 * @date 2021-09-16 8:25 下午
 **/
@Service
@DS("multi-datasource1")
@Slf4j
public class SugarRecordServiceImpl extends ServiceImpl<DtrdEntRdSugarValueMapper, DtrdEntRdSigninSugarValue> implements ISugarRecordService {

    @Autowired
    private IDtrdEntRdSigninRecordService signinRecordService;

    @Autowired
    private IDtrdViewRlCheckinRecordService checkinRecordService;
    @Autowired
    private IDtrdViewRlPatientSugarValueService patientSugarValueService;
    @Autowired
    private IDtrdEntRdPatientService patientService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IDtrdRlDoctorPatientService doctorPatientService;
    @Autowired
    private IDtrdEntRdUplowRecordService uplowRecordService;
    @Autowired
    private MsgEventPusher msgEventPusher;

    @Override
    public DtrdEntRdSigninSugarValue transformRecord(CheckinRecord record) {
        DtrdEntRdSigninSugarValue dt = new DtrdEntRdSigninSugarValue().init();
        dt.setSugarValueRemark(record.getRemark());
        dt.setDataId(record.getDataId());
        dt.setSugarValueTimeTypeId(record.getSubType());
        dt.setSugarValueSugarValue(record.getSugarValue());
        dt.setPatientId(record.getPatientId());
        dt.setRecordTime(parseStringAsDateTime(record.getRecordTime()));
        dt.setRecordDate(parseStringAsDate(record.getRecordTime()));
        dt.setRecordPhoto(record.getImages());
        return dt;
    }

    @Override
    public CheckinRecord transformPO(DtrdEntRdSigninSugarValue po) {
        if (po == null) {
            return null;
        }
        CheckinRecord record = new CheckinRecord();
        record.setRecordTime(DateUtils.formatTime(po.getRecordTime()));
        record.setRecordTimeHm(DateUtils.formatShortTime(po.getRecordTime()));
        record.setDataId(po.getDataId());
        record.setSubType(po.getSugarValueTimeTypeId());
        record.setImages(po.getRecordPhoto());
        record.setPatientId(po.getPatientId());
        record.setRecordType(Constant.RecordType.SUGAR.getType());
        record.setRecordTypeName(Constant.RecordType.SUGAR.getName());
        record.setRemark(po.getSugarValueRemark());
        record.setSugarValue(po.getSugarValueSugarValue());
        return record;
    }

    @Override
    public LambdaQueryWrapper<DtrdEntRdSigninSugarValue> generateSelectRecordByDateQuery(Integer patientId, String date, Integer subType) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> query = new LambdaQueryWrapper<>();
        query.eq(DtrdEntRdSigninSugarValue::getIsDel, 0).eq(DtrdEntRdSigninSugarValue::getPatientId, patientId)
                .eq(DtrdEntRdSigninSugarValue::getRecordDate, date).eq(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, subType);
        return query;
    }

    @Override
    public DtrdEntRdSigninSugarValue getSugarValueLatest(Integer patientId) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, patientId)
                .orderByDesc(DtrdEntRdSigninSugarValue::getRecordTime)
                .last("limit 1");
        return getOne(wrapper);
    }

    @Override
    public DtrdEntRdSigninSugarValue getSugarValueLatestByPatientIdAndType(Integer patientId, Integer... types) {
        List<Integer> queryTypes = Arrays.stream(types).collect(Collectors.toList());
        LambdaUpdateWrapper<DtrdEntRdSigninSugarValue> wrapper = Wrappers.lambdaUpdate(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, patientId)
                .in(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, queryTypes)
                .orderByDesc(DtrdEntRdSigninSugarValue::getCreateTime)
                .last("limit 1");
        return getOne(wrapper);
    }

    @Override
    public boolean saveSugarRecord(SugarValueBean value) {
        Integer patientId = value.getPatientId();
        DtrdEntRdSigninSugarValue newRecord = new DtrdEntRdSigninSugarValue();
        if (value.getId() == null) {
            newRecord.init();
        }
        newRecord.setPatientId(patientId);
        // 记录一下 yyyy-MM-dd 更新的日期
        Date recordDate = null;
        try {
            recordDate = DateUtils.parseDate(value.getTime(), DateUtils.date_sdf.get().toPattern());
            newRecord.setRecordTime(DateUtils.parseDate(value.getTime(), DateUtils.datetimeFormat.get().toPattern()));
            newRecord.setRecordDate(recordDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        newRecord.setSugarValueType(SchemeUtil.compareSugarStatus(value.getValue(), value.getType(), patientId));
        newRecord.setSugarValueSugarValue(value.getValue());
        newRecord.setSugarValueTimeTypeId(value.getType());
        newRecord.setSugarValueRemark(value.getRemark());
        newRecord.setSugarValueSourceType(value.getSourceType());
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, value.getPatientId())
                .eq(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, value.getType())
                .eq(DtrdEntRdSigninSugarValue::getRecordDate, recordDate);
        boolean result = saveOrUpdate(newRecord, wrapper);
        return result;
    }

    @Override
    public SugarChart getBloodSugarRecordsForCharts(int patientId, String startDate, String endDate, int sourceType) {
        List<DtrdEntRdSigninSugarValue> list = baseMapper.getBloodSugarRecordsForCharts(patientId, startDate, endDate, sourceType);
        log.info("SugarRecordServiceImpl.getBloodSugarRecordsForCharts.[patientId, startDate, endDate] list.size = " + list.size());
        SugarChart sugarChart = new SugarChart();
        // dates 返回yyyy-MM-dd HH:mm:ss
        List<String> dates = new ArrayList<>(list.size());
        List<String> datesAll = new ArrayList<>(list.size());
        // values1-8 分别代表0早餐前、1早餐后、2午餐前、3午餐后、4晚餐前、5晚餐后、6睡前、7凌晨
        List<String> values1 = new ArrayList<>(list.size());
        List<String> values2 = new ArrayList<>(list.size());
        List<String> values3 = new ArrayList<>(list.size());
        List<String> values4 = new ArrayList<>(list.size());
        List<String> values5 = new ArrayList<>(list.size());
        List<String> values6 = new ArrayList<>(list.size());
        List<String> values7 = new ArrayList<>(list.size());
        List<String> values8 = new ArrayList<>(list.size());
        List<String> values0 = new ArrayList<>(list.size());
        Map<String, Map<Integer, Float>> tmp = new LinkedHashMap<>();
        Map<Integer, Float> tmpValue;
        String date, time;
        Integer recordType;
        float value;
        for (DtrdEntRdSigninSugarValue po : list) {
            date = DateUtils.formatDate(po.getRecordDate());
            String dateStr = DateUtils.date2Str(po.getRecordTime(),DateUtils.datetimeFormat.get());
            time = DateUtils.formatDate(po.getRecordTime(), DateUtils.datetimeFormat.get().toPattern());
            recordType = po.getSugarValueTimeTypeId();
            value = po.getSugarValueSugarValue();
            values0.add(String.valueOf(value));
            tmpValue = tmp.get(date);
            if (tmpValue == null) {
                tmpValue = new HashMap<>(list.size() / 2);
                dates.add(time);
            }
            datesAll.add(time);
            tmpValue.put(recordType, value);
            tmp.put(dateStr, tmpValue);
        }
        Set<String> keySet = tmp.keySet();
        for (String key : keySet) {
            tmpValue = tmp.get(key);
            values1.add(tmpValue.containsKey(Constant.SugarRecordType.BEFOREBREAKFAST.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.BEFOREBREAKFAST.getType())) : "''");
            values2.add(tmpValue.containsKey(Constant.SugarRecordType.AFTERBREAKFAST.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.AFTERBREAKFAST.getType())) : "''");
            values3.add(tmpValue.containsKey(Constant.SugarRecordType.BEFORELUNCH.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.BEFORELUNCH.getType())) : "''");
            values4.add(tmpValue.containsKey(Constant.SugarRecordType.AFTERLUNCH.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.AFTERLUNCH.getType())) : "''");
            values5.add(tmpValue.containsKey(Constant.SugarRecordType.BEFOREDINNER.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.BEFOREDINNER.getType())) : "''");
            values6.add(tmpValue.containsKey(Constant.SugarRecordType.AFTERDINNER.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.AFTERDINNER.getType())) : "''");
            values7.add(tmpValue.containsKey(Constant.SugarRecordType.BEFORESLEEP.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.BEFORESLEEP.getType())) : "''");
            values8.add(tmpValue.containsKey(Constant.SugarRecordType.EARLYMORNING.getType()) ? String.valueOf(tmpValue.get(Constant.SugarRecordType.EARLYMORNING.getType())) : "''");
        }
        sugarChart.setDates(datesAll);
        sugarChart.setDatesAll(datesAll);
        sugarChart.setValue0(values0);
        sugarChart.setValue1(values1);
        sugarChart.setValue2(values2);
        sugarChart.setValue3(values3);
        sugarChart.setValue4(values4);
        sugarChart.setValue5(values5);
        sugarChart.setValue6(values6);
        sugarChart.setValue7(values7);
        sugarChart.setValue8(values8);
        List<List<String>> valueList = new ArrayList<>(8);
        valueList.add(values0);
        valueList.add(values1);
        valueList.add(values2);
        valueList.add(values3);
        valueList.add(values4);
        valueList.add(values5);
        valueList.add(values6);
        valueList.add(values7);
        valueList.add(values8);
        sugarChart.setValue(valueList);
        sugarChart.setSugarStatisticsBean(getPatientSugarStatisticsValue(patientId, startDate, endDate));
        return sugarChart;
    }

    @Override
    public Integer getSugarRecordDateCount(Integer patientId, String startDate, String endDate, Integer sourceType) {
        return baseMapper.getSugarRecordDateCount(patientId, startDate, endDate, sourceType);
    }

    @Override
    public List<DailySugarRecordBean> getAllGroupedSugarValues(Integer patientId, String startDate, String endDate, Integer pageNum, Integer pageSize) {
        Integer pageStart = (pageNum != null && pageSize != null) ? (pageNum - 1) * pageSize : null;
        List<DtrdEntRdSigninSugarValue> list = baseMapper.getAllGroupedSugarValues(patientId, startDate, endDate, 0);
        if (list.size() > 0) {
            List<DailySugarRecordBean> dataList = new ArrayList<>(list.size() / 2);
            TreeMap<String, StringJoiner> resultMap = new TreeMap<>(Collections.reverseOrder());
            list.forEach(value -> {
                String date = DateUtils.formatDate(value.getRecordDate());
                StringJoiner sj = resultMap.get(date);
                if (sj == null) {
                    sj = new StringJoiner("@");
                }
                sj.add(formatSugarTypeSugarValue(StringUtil.StrTrimInt(value.getSugarValueTimeTypeId()),
                        value.getDataId(),
                        value.getSugarValueSugarValue(),
                        value.getRecordTime(),
                        StringUtil.StrTrim(value.getSugarValueRemark()),
                        StringUtil.StrTrim(value.getRecordPhoto())));
                resultMap.put(date, sj);
            });
            resultMap.descendingKeySet();
            if (!CheckUtil.isEmpty(resultMap)) {
                Set<String> keySet = resultMap.keySet();
                keySet.forEach(date -> {
                    StringJoiner sj = resultMap.get(date);
                    if (sj != null && sj.length() > 0) {
                        String str = sj.toString();
                        if (!CheckUtil.isEmpty(str)) {
                            DailySugarRecordBean record = new DailySugarRecordBean();
                            String formatDate = date;
                            record.setFormatDate(formatDate);
                            date = DateUtils.changeDateFormat(date, DateUtils.date_sdf.get(), DateUtils.MMdd.get());
                            record.setDate(date);
                            String[] split = str.split("@");
                            String[] split2;
                            float value;
                            for (String tmp : split) {
                                split2 = tmp.split("!");
                                if (split2.length == 2) {
                                    SugarValueBean sugarValue = method(split2[1].split("="));
                                    value = StringUtil.StrTrimFloat(sugarValue.getValue());
                                    int type = StringUtil.StrTrimInt(split2[0]);
                                    sugarValue.setType(type);
                                    Constant.SugarRecordType recordType = Constant.SugarRecordType.valueOf(type);
                                    switch (recordType) {
                                        case BEFOREBREAKFAST:
                                            record.setBeforeBreakfast(value);
                                            record.setBeforeBreakfastRecord(sugarValue);
                                            break;
                                        case AFTERBREAKFAST:
                                            record.setAfterBreakfast(value);
                                            record.setAfterBreakfastRecord(sugarValue);
                                            break;
                                        case BEFORELUNCH:
                                            record.setBeforeLunch(value);
                                            record.setBeforeLunchRecord(sugarValue);
                                            break;
                                        case AFTERLUNCH:
                                            record.setAfterLunch(value);
                                            record.setAfterLunchRecord(sugarValue);
                                            break;
                                        case BEFOREDINNER:
                                            record.setBeforeDinner(value);
                                            record.setBeforeDinnerRecord(sugarValue);
                                            break;
                                        case AFTERDINNER:
                                            record.setAfterDinner(value);
                                            record.setAfterDinnerRecord(sugarValue);
                                            break;
                                        case BEFORESLEEP:
                                            record.setBeforeSleep(value);
                                            record.setBeforeSleepRecord(sugarValue);
                                            break;
                                        case EARLYMORNING:
                                            record.setEarlyMorning(value);
                                            record.setEarlyMorningRecord(sugarValue);
                                            break;
                                        default:
                                            record.setRandomTime(value);
                                            record.setRandomTimeRecord(sugarValue);
                                            break;
                                    }
                                }
                            }
                            dataList.add(record);
                        }
                    }
                });
                ArrayList<DailySugarRecordBean> pageList = new ArrayList<>(pageSize);
                for (int i = pageStart; i < pageSize * pageNum; i++) {
                    if (i >= dataList.size()) {
                        break;
                    }
                    pageList.add(dataList.get(i));
                }
                return pageList;
            }
        }
        return Collections.EMPTY_LIST;
    }

    public static SugarValueBean method(String[] split) {
        if (!CheckUtil.isEmpty(split)) {
            SugarValueBean value = new SugarValueBean();
            value.setId(StringUtil.StrTrimInt(split[0]));
            value.setValue(StringUtil.StrTrimFloat(split[1]));
            if (split.length >= 3) {
                value.setTime(split[2]);
            }
            if (split.length >= 4) {
                value.setRemark(split[3]);
            }
            if (split.length >= 5) {
                value.setRecordPhoto(split[4]);
            }
            return value;
        }
        return null;
    }

    private static String formatSugarTypeSugarValue(int timeType, int dataId, float sugarValue, Date sugarValuesRecordTime, String sugarValuesRemarks, String recordPhoto) {
        String format = "%d!%d=%.1f=%s=%s=%s";
        return String.format(format, timeType, dataId, sugarValue,
                DateUtils.formatDate(sugarValuesRecordTime, DateUtils.datetimeFormat.get().toPattern()),
                sugarValuesRemarks, recordPhoto);
    }

    /**
     * 查询血糖统计数据 按照血糖记录类别进行统计， 总次数，最高、最低值、平均值、偏高、偏低、正常次数
     *
     * @param patientId
     * @param type
     * @return
     */
    @Override
    public Map getBloodSugarStatisticsByRecordType(int patientId, int type, String startDate, String endDate, int period) {
        Map map = new HashMap(16);
        try {
            String[] timePeriod = handleTimePeriod(startDate, endDate, patientId, type);
            if (timePeriod != null) {
                startDate = timePeriod[0];
                endDate = timePeriod[1];
            }
            // 先检测时间段内是否有数据 ，避免返回全0的记录
            Integer count = getSugarRecordCountByPatientIdDate(patientId, startDate, endDate);
            if (StringUtil.StrTrimInt(count) == 0) {
                log.info("getBloodSugarStatisticsByRecordType." + patientId + " no record between " + startDate + " and " + endDate);
            } else {
                List<SugarStatisticsBean> list = new ArrayList<SugarStatisticsBean>(9);
                // 空腹
                SugarStatisticsBean sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate,
                        endDate, Constant.SugarRecordType.BEFOREBREAKFAST.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.BEFOREBREAKFAST.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);
                // 早餐后
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.AFTERBREAKFAST.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.AFTERBREAKFAST.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);

                // 午餐前
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.BEFORELUNCH.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.BEFORELUNCH.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);
                // 午餐后
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.AFTERLUNCH.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.AFTERLUNCH.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);

                // 晚餐前
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.BEFOREDINNER.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.BEFOREDINNER.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);
                // 晚餐后
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.AFTERDINNER.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.AFTERDINNER.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);

                // 睡前
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.BEFORESLEEP.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.BEFORESLEEP.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);
                // 凌晨
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.EARLYMORNING.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.EARLYMORNING.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);

                // 随机
                sugarStatisticsBean = getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate,
                        Constant.SugarRecordType.RANDOMTIME.getType());
                sugarStatisticsBean.setType(Constant.SugarRecordType.RANDOMTIME.getType());
                sugarStatisticsBean.setPatientId(patientId);
                list.add(sugarStatisticsBean);
                map.put("statistics", list);
            }
        } catch (Exception e) {
            log.error("血糖统计异常", e);
        }
        return map;
    }

    @Override
    public SugarValueBean selectLastRecordByPatientId(Integer patientId) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> queryWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getIsDel, 0)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, patientId)
                // TODO 暂时不查找凌晨类型的血样记录
                .notIn(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, Constant.SugarRecordType.EARLYMORNING.getType())
                .orderByDesc(DtrdEntRdSigninSugarValue::getRecordDate)
                .orderByDesc(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId)
                .last("limit 1");
        DtrdEntRdSigninSugarValue po = getBaseMapper().selectOne(queryWrapper);
        return Optional.ofNullable(po).map(p -> new SugarValueBean().parseFromPo(p)).orElse(null);
    }

    @Override
    public boolean synchSGBLSugarList(List<SugarValueBean> sugarList, Integer sourceType, Integer patientId) {
        String datePattern = DateUtils.datetimeFormat.get().toPattern();
        // 空列表不做处理
        if (CollectionUtil.isEmpty(sugarList)) {
            return true;
        }
        // 设置时间和类型
        sugarList.parallelStream().forEach(value -> {
            if (StringUtil.isValidNumber(value.getTime())) {
                int hour = StringUtil.StrTrimInt(TimeUtil.getInstance().getFormatedDateTime("HH", StringUtil.StrTrimLong(value.getTime())));
                value.setTime(TimeUtil.getInstance().getFormatedDateTime(datePattern, StringUtil.StrTrimLong(value.getTime())));
                value.setType(SugarUtil.getTimeTypeIndex(hour));
            }
        });
        String time = sugarList.get(0).getTime();
        int judgeDate = TimeUtil.getInstance().judgeDate(time, sugarList.get(sugarList.size() - 1).getTime(), datePattern);
        if (judgeDate > 0) {
            time = sugarList.get(sugarList.size() - 1).getTime();
        }
        // 保存数据
        List<DtrdEntRdSigninSugarValue> poList = sugarList.stream()
                .map(sugar -> new DtrdEntRdSigninSugarValue().parseFromDTO(sugar))
                .collect(Collectors.toList());
        boolean result = saveBatch(poList);
        // 删除错误数据: 未来的数据
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, patientId)
                .eq(DtrdEntRdSigninSugarValue::getSugarValueSourceType, Constant.SugarSourceType.SOURCE_SGBL_BLE.getType())
                .ge(DtrdEntRdSigninSugarValue::getRecordTime, time);
        List<DtrdEntRdSigninSugarValue> futureRecords = list(wrapper);
        if (CollectionUtil.isNotEmpty(futureRecords)) {
            List<Integer> dataIds = futureRecords.stream()
                    .map(DtrdEntRdSigninSugarValue::getDataId).collect(Collectors.toList());
            removeByIds(dataIds);
        }
        return result;
    }

    @Override
    public DtrdEntRdSigninSugarValue getXtySugarRecord(Integer userId, Integer recordType, String uploadTime, int timeType, Double sugar) {
        return baseMapper.getXtySugarRecord(userId, recordType, uploadTime, timeType, sugar);
    }

    /**
     * 按类型计算平均值
     *
     * @param patientId
     * @param startOfWeek
     * @param endOfWeek
     * @param timeTypes   时段类型，如空腹：(1)、餐后(2,4,6)
     * @return
     */
    @Override
    public Float getAvgSugarRecord(Integer patientId, String startOfWeek, String endOfWeek, String timeTypes) {
        return baseMapper.getAvgSugarRecordByTimeTypes(patientId, startOfWeek, endOfWeek, timeTypes);
    }

    @Override
    public Float getFirstSugarValue(Integer patientId, String timeTypes) {
        return baseMapper.getFirstSugarValue(patientId, timeTypes);
    }

    /**
     * 计算时间段内各时段血糖平均值
     *
     * @param patientId
     * @param startDate
     * @param endDate
     * @return
     */
    @Override
    public List<AvgRecordBean> getAvgSugarRecord(Integer patientId, String startDate, String endDate) {
        return baseMapper.getAvgSugarRecord(patientId, startDate, endDate);
    }

    @Override
    public void onWeiceSugarRecord(Integer userId, Float value, int timeType, String measureTime, Integer measureResult) {
        if (timeType == 4) {
            // 微策空腹记录
            timeType = Constant.SugarRecordType.BEFOREBREAKFAST.getType();
        } else if (timeType == 8) {
            // 微策凌晨记录
            timeType = Constant.SugarRecordType.EARLYMORNING.getType();
        } else {
            timeType = SGBLBLEParseUtil.getHourFromTime(measureTime);
        }
        saveSugarRecord(userId, value, measureTime, timeType, Constant.SugarSourceType.SOURCE_WEICE.getType());
    }

    @Override
    public void saveXtySugarRecord(List<XtySugarRecord> recordList, Integer userId) {
        List<DtrdEntRdSigninSugarValue> list = new ArrayList<>(recordList.size());
        recordList.stream().map(xtyRecord -> {
            DtrdEntRdSigninSugarValue value = new DtrdEntRdSigninSugarValue().init().parseFromXtyRecord(xtyRecord);
            value.setPatientId(userId);
            return value;
        }).forEach(list::add);
        boolean b = saveBatch(list);
        if (b) {
            DtrdEntRdSigninSugarValue sugarValuesVO = list.get(0);
            doctorPatientService.onSugarRecord(sugarValuesVO.getPatientId(), sugarValuesVO.getDataId(), sugarValuesVO.getRecordDate());
        }
        log.info("SugarRecordServiceImpl.saveXtySugarRecord.[recordList, userId = " + userId + " ; size=" + recordList.size() + "; 保存数据结果  " + (b ? "成功" : "失败") + "; list.get(0).getDataId()=" + list.get(0).getDataId());
    }

    @Override
    public void saveBiolandRecord(SrcDataValue srcDatavalue, Integer userId) {
        String checkTime = srcDatavalue.getCheckTime();
        int timeType = SGBLBLEParseUtil.getHourFromTime(checkTime);
        saveSugarRecord(userId, parseSugarValueFrommg2mmol(srcDatavalue.getSugarValue()), checkTime, timeType, Constant.SugarSourceType.SOURCE_BIOLAND.getType());
    }

    @Override
    public List<DailySugarRecordBean> getDailySugarRecord(Integer patientId, String startDate, String endDate, Integer pageNum, Integer pageSize) {
        Integer pageStart = (pageNum != null && pageSize != null) ? (pageNum - 1) * pageSize : null;
        return checkinRecordService.getDailySugarRecord(patientId, startDate, endDate, pageStart, pageSize);
    }

    @Override
    public List<DailySugarRecordBean> getSGBLSugarRecord(Integer patientId, String startDate, String endDate, Integer page, Integer pageSize, Integer sourceType) {
        Integer pageStart = (page != null && pageSize != null) ? (page - 1) * pageSize : null;
        return getDailySugarRecord(patientId, startDate, endDate, pageStart, pageSize, sourceType);
    }

    @Override
    public SugarValueLineData pageSugarRecords(Integer patientId, Date startTime, Date endTime, Integer timeType, Integer sourceType, Integer orderType) {
        List<SugarValueLineBean> sugarValues = baseMapper.pageSugarRecords(patientId, startTime, endTime, timeType, sourceType, orderType);
        SugarValueLineData result = new SugarValueLineData(patientId, startTime, endTime);
        List<String> values = sugarValues.stream().map(po -> po.getSugarValue().toString()).collect(Collectors.toList());
        List<String> dates = sugarValues.stream().map(SugarValueLineBean::getRecordTime).collect(Collectors.toList());
        result.setSugarValues(values);
        result.setDates(dates);
        return result;
    }

    @Override
    public PatientDailySugar getPatientDailySugar(Integer patientId, Date date) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> wrapper1 = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, patientId)
                .eq(DtrdEntRdSigninSugarValue::getRecordDate, date)
                .in(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId,
                        Constant.SugarRecordType.BEFOREBREAKFAST.getType(),
                        Constant.SugarRecordType.AFTERBREAKFAST.getType(),
                        Constant.SugarRecordType.BEFORELUNCH.getType(),
                        Constant.SugarRecordType.AFTERLUNCH.getType(),
                        Constant.SugarRecordType.BEFOREDINNER.getType(),
                        Constant.SugarRecordType.AFTERDINNER.getType(),
                        Constant.SugarRecordType.BEFORESLEEP.getType(),
                        Constant.SugarRecordType.EARLYMORNING.getType()
                )
                .orderByAsc(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId);
        List<DtrdEntRdSigninSugarValue> sugarRecords = list(wrapper1);
        if (CollectionUtil.isEmpty(sugarRecords)) {
            return null;
        }
        PatientDailySugar patientDailySugar = new PatientDailySugar();
        int initCapacity = 8;
        List<Float> sugarValues = new ArrayList<>(initCapacity);
        List<String> sugarRecordTimes = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(sugarRecords)) {
            for (int i = 0; i < initCapacity; i++) {
                sugarValues.add(null);
                sugarRecordTimes.add("");
            }
            for (DtrdEntRdSigninSugarValue sugarRecord : sugarRecords) {
                Integer timeTypeId = sugarRecord.getSugarValueTimeTypeId();
                sugarValues.set(timeTypeId - 1, sugarRecord.getSugarValueSugarValue());
                sugarRecordTimes.set(timeTypeId - 1, DateUtils.formatDate(sugarRecord.getRecordTime(), "HH:mm"));
            }
        }
        patientDailySugar.setSugarValues(sugarValues);
        patientDailySugar.setSugarTimes(sugarRecordTimes);
        return patientDailySugar;
    }

    @Override
    public List<DtrdEntRdSigninSugarValue> listSugarValuesByPatientIds(List<Integer> patientIds) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .in(DtrdEntRdSigninSugarValue::getPatientId, patientIds);
        return list(wrapper);
    }

    private static final String SUGAR_COUNT = "sugarCount";

    private static final String SUGAR_ABNORMAL_COUNT = "sugarAbnormalCount";

    @Override
    public DoctorIndexDynamicInfo listPatientSugarValuesInfo(List<Integer> patientIds) {
        LambdaQueryWrapper<DtrdViewRlPatientSugarValue> wrapper = Wrappers.lambdaQuery(DtrdViewRlPatientSugarValue.class)
                .in(DtrdViewRlPatientSugarValue::getPatientId, patientIds)
                .ge(DtrdViewRlPatientSugarValue::getRecordDate, DateUtils.getDateYMD())
                .orderByDesc(DtrdViewRlPatientSugarValue::getRecordTime);
        List<DtrdViewRlPatientSugarValue> sugarValues = patientSugarValueService.list(wrapper);
        DoctorIndexDynamicInfo dynamicInfo = new DoctorIndexDynamicInfo();
        if (CollectionUtil.isNotEmpty(sugarValues)) {
            Map<Integer, String> map = patientService.getPatientIdToNameMap(patientIds);
            // 总值
            int todayCount = sugarValues.size();
            dynamicInfo.setTodayCount(todayCount);
            redisUtil.set(SUGAR_COUNT, todayCount);
            // 总值差值
            Integer oldCount = (Integer) redisUtil.get(SUGAR_COUNT);
            if (oldCount != null) {
                dynamicInfo.setTodayCountDifference(todayCount - oldCount);
            }
            // 记录各情况数据
            AtomicInteger lowCount = new AtomicInteger();
            AtomicInteger highCount = new AtomicInteger();
            AtomicInteger normalCount = new AtomicInteger();
            sugarValues.forEach(sugarValue -> {
                Integer sugarValueType = sugarValue.getSugarValueType();
                Constant.RecordStatus type = Constant.RecordStatus.valueOf(sugarValueType);
                switch (type) {
                    case LOWER:
                        lowCount.getAndIncrement();
                        break;
                    case HIGHER:
                        highCount.getAndIncrement();
                        break;
                    case NORMAL:
                        normalCount.getAndIncrement();
                        break;
                }
            });
            int abnormalCount = lowCount.get() + highCount.get();
            dynamicInfo.setTodayAbnormalCount(abnormalCount);
            redisUtil.set(SUGAR_ABNORMAL_COUNT, abnormalCount);
            // 异常值差值
            Integer oldAbnormalCount = (Integer) redisUtil.get(SUGAR_ABNORMAL_COUNT);
            if (oldAbnormalCount != null) {
                dynamicInfo.setTodayAbnormalCountDifference(abnormalCount - oldAbnormalCount);
            }
            // 血糖状况饼状图
            List<PieChartItem> items = new ArrayList<>(3);
            items.add(new PieChartItem("血糖正常", normalCount.get()));
            items.add(new PieChartItem("血糖偏高", highCount.get()));
            items.add(new PieChartItem("血糖偏低", lowCount.get()));
            dynamicInfo.setSugarStatus(items);
            // 血糖记录
            List<DoctorIndexDynamicInfo.PatientSugarValueInfo> valueInfos = sugarValues.stream().map(sugarValue -> {
                Integer patientId = sugarValue.getPatientId();
                String name = map.get(patientId);
                return new DoctorIndexDynamicInfo.PatientSugarValueInfo()
                        .parseFromPO(sugarValue)
                        .buildWithName(name);
            }).limit(6).collect(Collectors.toList());
            dynamicInfo.setValueInfos(valueInfos);
            return dynamicInfo;
        }
        return null;
    }

    private List<DailySugarRecordBean> getDailySugarRecord(Integer patientId, String startDate, String endDate, Integer pageStart, Integer pageSize, Integer sourceType) {
        List<DtrdEntRdSigninSugarValue> list = baseMapper.getAllGroupedSugarValues(patientId, startDate, endDate, sourceType);
        if (list.size() > 0) {
            List<DailySugarRecordBean> dataList = new ArrayList<>(list.size() / 2);
            // 转换成 date : 各阶段photo 的映射
            TreeMap<String, List<DtrdEntRdSigninSugarValue>> recordMap = new TreeMap<>(Collections.reverseOrder());
            list.forEach(value -> {
                String date = DateUtils.formatDate(value.getRecordDate());
                List<DtrdEntRdSigninSugarValue> dateRecordList = recordMap.get(date);
                if (dateRecordList == null) {
                    dateRecordList = new ArrayList<>(8);
                }
                dateRecordList.add(value);
                recordMap.put(date, dateRecordList);
            });
            if (MapUtil.isNotEmpty(recordMap)) {
                Set<String> keySet = recordMap.keySet();
                keySet.forEach(date -> {
                    DailySugarRecordBean dailySugarRecordBean = new DailySugarRecordBean();
                    dailySugarRecordBean.setDate(date);
                    dailySugarRecordBean.setPatientId(patientId);
                    List<DtrdEntRdSigninSugarValue> dataRecord = recordMap.get(date);
                    for (DtrdEntRdSigninSugarValue sugarRecord : dataRecord) {
                        Constant.SugarRecordType type = Constant.SugarRecordType.valueOf(sugarRecord.getSugarValueTimeTypeId());
                        assert type != null;
                        switch (type) {
                            case BEFOREBREAKFAST:
                                dailySugarRecordBean.setBeforeBreakfast(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setBeforeBreakfastRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case AFTERBREAKFAST:
                                dailySugarRecordBean.setAfterBreakfast(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setAfterBreakfastRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case BEFORELUNCH:
                                dailySugarRecordBean.setBeforeLunch(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setBeforeLunchRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case AFTERLUNCH:
                                dailySugarRecordBean.setAfterLunch(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setAfterLunchRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case BEFOREDINNER:
                                dailySugarRecordBean.setBeforeDinner(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setBeforeDinnerRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case AFTERDINNER:
                                dailySugarRecordBean.setAfterDinner(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setAfterDinnerRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case BEFORESLEEP:
                                dailySugarRecordBean.setBeforeSleep(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setBeforeSleepRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                            case EARLYMORNING:
                                dailySugarRecordBean.setEarlyMorning(sugarRecord.getSugarValueSugarValue());
                                dailySugarRecordBean.setEarlyMorningRecord(new SugarValueBean().parseFromPo(sugarRecord));
                                break;
                        }
                    }
                    dataList.add(dailySugarRecordBean);
                });
                // controller 层会有默认的 page, pageSize
                return dataList.stream().skip(pageStart).limit(pageSize).collect(Collectors.toList());
            }
        }
        return null;
    }

    /**
     * 统计某时间段内莫类型血糖信息
     *
     * @param patientId
     * @param startDate
     * @param endDate
     * @param type
     * @return
     */
    private SugarStatisticsBean getPatientSugarStatisticsValueByRecordType(int patientId, String startDate, String endDate, int type) {
        return baseMapper.getPatientSugarStatisticsValueByRecordType(patientId, startDate, endDate, type);
    }

    /**
     * 先检测时间段内是否有数据 ，避免返回全0的记录
     *
     * @param patientId
     * @param startDate
     * @param endDate
     * @return
     */
    private Integer getSugarRecordCountByPatientIdDate(int patientId, String startDate, String endDate) {
        return baseMapper.getSugarRecordCountByPatientIdDate(patientId, startDate, endDate);
    }

    /**
     * 处理请求时间
     *
     * @param
     * @param type 0、2直接获取参数值，3 以当天为结束日期按照指定周期倒推开始时间
     * @return
     */
    private String[] handleTimePeriod(String startDate, String endDate, int period, int type) {
        String[] timePeriod = null;
        if (type == 0 || type == 2) {
            if (endDate.equals("")) {
                endDate = startDate;
            }
            timePeriod = new String[2];
            timePeriod[0] = startDate;
            timePeriod[1] = endDate;
        } else if (type == 3) {
            // 按照周期查询 0：全部 1：一周、2：两周、3：一月、4：两月 5 三月 6 半年 7 一年
            endDate = DateUtils.formatDate();
            int dateSub = 0;
            switch (period) {
                case 0:
                    dateSub = Integer.MIN_VALUE;
                    break;
                case 1:
                    dateSub = -6;
                    break;
                case 2:
                    dateSub = -13;
                    break;
                case 3:
                    dateSub = -29;
                    break;
                case 4:
                    dateSub = -59;
                    break;
                case 5:
                    dateSub = -89;
                    break;
                case 6:
                    dateSub = -179;
                    break;
                case 7:
                    dateSub = -364;
                    break;
                default:
                    dateSub = Integer.MIN_VALUE;
                    break;
            }
            if (dateSub == Integer.MIN_VALUE) {
                startDate = "1970-01-01";
            } else {
                startDate = DateUtils.dateAdd(dateSub);
            }
            timePeriod = new String[2];
            timePeriod[0] = startDate;
            timePeriod[1] = endDate;
        }
        return timePeriod;
    }


    private SugarStatistics getPatientSugarStatisticsValue(int patientId, String startDate, String endDate) {
        SugarStatistics statistics = new SugarStatistics();
        float limitLow = 4.4f;
        float limitHigh = 6.1f;
        StringJoiner sj = new StringJoiner(",", "(", ")");
        // 按照餐前统计
        sj.add("" + Constant.SugarRecordType.BEFOREBREAKFAST.getType()).add("" + Constant.SugarRecordType.BEFORELUNCH.getType())
                .add("" + Constant.SugarRecordType.BEFOREDINNER.getType()).add("" + Constant.SugarRecordType.BEFORESLEEP.getType())
                .add("" + Constant.SugarRecordType.EARLYMORNING.getType());
        SugarStatisticsBean statisticsBean = baseMapper.selectSugarStatisticsByRecordTypes(patientId, sj.toString(), startDate, endDate, limitLow, limitHigh);
        int highCounts = 0, lowCounts = 0, normalCounts = 0;
        float highValue = 0, lowValue = 0, avgValue = 0f;
        if (statisticsBean != null) {
            highCounts = StringUtil.StrTrimInt(statisticsBean.getHighCounts());
            lowCounts = StringUtil.StrTrimInt(statisticsBean.getLowCounts());
            normalCounts = StringUtil.StrTrimInt(statisticsBean.getNormalCounts());
            highValue = StringUtil.StrTrimFloat(statisticsBean.getHighestValue());
            lowValue = StringUtil.StrTrimFloat(statisticsBean.getLowestValue());
            avgValue = StringUtil.StrTrimFloat(statisticsBean.getAvgValue());
        }
        int avgCount = highCounts + lowCounts + normalCounts;
//            LogUtil.print("DdoctorXtOperation.getPatientSugarStatisticsValueV4.[patientId, startDate, endDate] avgValue = " + avgValue);
        // 按照餐后统计
        sj = new StringJoiner(",", "(", ")");
        sj.add("" + Constant.SugarRecordType.AFTERBREAKFAST.getType()).add("" + Constant.SugarRecordType.AFTERDINNER.getType())
                .add("" + Constant.SugarRecordType.AFTERLUNCH.getType());
        limitHigh = 7.8f;
        SugarStatisticsBean statisticsBean2 = baseMapper.selectSugarStatisticsByRecordTypes(patientId, sj.toString(), startDate, endDate, limitLow, limitHigh);
//            LogUtil.print("DdoctorXtOperation.getPatientSugarStatisticsValueV4.statisticsBean2=" + statisticsBean2);
        if (statisticsBean2 != null) {
            int highCounts2 = StringUtil.StrTrimInt(statisticsBean2.getHighCounts());
            highCounts += highCounts2;
            int lowCounts2 = StringUtil.StrTrimInt(statisticsBean2.getLowCounts());
            lowCounts += lowCounts2;
            int normalCounts2 = StringUtil.StrTrimInt(statisticsBean2.getNormalCounts());
            normalCounts += normalCounts2;
            float avgValue2 = StringUtil.StrTrimFloat(statisticsBean2.getAvgValue());
            highValue = Math.max(highValue, StringUtil.StrTrimFloat(statisticsBean2.getHighestValue()));
            lowValue = Math.min(lowValue, StringUtil.StrTrimFloat(statisticsBean2.getLowestValue()));
            if (avgValue2 > 0f) {
                int tmpCount = highCounts2 + lowCounts2 + normalCounts2 + avgCount;
                avgValue = (avgValue * avgCount + avgValue2 * (highCounts2 + lowCounts2 + normalCounts2)) / tmpCount;
            }
        }
        statistics.setHighCounts(highCounts);
        statistics.setLowCounts(lowCounts);
        statistics.setNormalCounts(normalCounts);
        statistics.setHighestValue(highValue);
        statistics.setLowestValue(lowValue);
        statistics.setAvgValue(avgValue);
        int count = lowCounts + highCounts + normalCounts;
        statistics.setCount(count);
        statistics.setLowPercent(calculatePercentage(lowCounts, count));
        statistics.setHighPercent(calculatePercentage(highCounts, count));
        statistics.setNormalPercent(100 - statistics.getLowPercent() - statistics.getHighPercent());

        return statistics;
    }

    public static Float calculatePercentage(double value, double count) {
        float result = 0f;
        if (value > 0 && count > 0) {
            if (value == count) {
                result = 100;
            } else {
                result = new BigDecimal((value * 100) / count).setScale(1, RoundingMode.HALF_UP).floatValue();
            }
        }
        return result;
    }

    private static float parseSugarValueFrommg2mmol(String mgValue) {
        float v = Float.parseFloat(mgValue);
        return v / 18;
    }

    /**
     * 保存血糖记录
     *
     * @param patientId
     * @param sugarValue
     * @param recordTime
     * @param timeType
     * @param sourceType
     */
    private void saveSugarRecord(Integer patientId, Float sugarValue, String recordTime, int timeType, Integer sourceType) {
        DtrdEntRdSigninSugarValue sugarValuesVO = new DtrdEntRdSigninSugarValue().init();
        sugarValuesVO.setPatientId(patientId);
        sugarValuesVO.setSugarValueSourceType(sourceType);
        sugarValuesVO.setSugarValueTimeTypeId(timeType);
        sugarValuesVO.setRecordTime(DateUtils.str2Date(recordTime, DateUtils.datetimeFormat.get()));
        sugarValuesVO.setRecordDate(DateUtils.str2Date(recordTime, DateUtils.date_sdf.get()));
        sugarValuesVO.setSugarValueSugarValue(sugarValue);
        saveOrUpdate(sugarValuesVO);
    }

    @Override
    public boolean saveOrUpdate(DtrdEntRdSigninSugarValue entity, Wrapper<DtrdEntRdSigninSugarValue> updateWrapper) {
        log.info("SugarRecordServiceImpl.saveOrUpdate.[entity, updateWrapper] " + entity);
        handleRecordStatus(entity);
        boolean result = this.update(entity, updateWrapper);
        if (result) {
            noticeSugarRecordChange(entity);
        } else {
            result = this.saveOrUpdate(entity);
        }
        return result;
    }

    @Override
    public boolean saveOrUpdate(DtrdEntRdSigninSugarValue entity) {
        handleRecordStatus(entity);
        boolean result = super.saveOrUpdate(entity);
        if (result) {
            noticeSugarRecordChange(entity);
        }
        return result;
    }

    @Override
    public boolean save(DtrdEntRdSigninSugarValue entity) {
        if (Optional.ofNullable(entity.getSugarValueType()).orElse(0) == 0) {
            // 没有记录状态 查询上下限进行判断
            float limitUp, limitLow;
            ValueUplowBean upLowRecord = uplowRecordService.getSugarValueUpLowRecord(entity.getPatientId());
            if (entity.getSugarValueTimeTypeId() == SugarRecordType.BEFOREBREAKFAST.getType()) {
                // 空腹
                limitLow = upLowRecord.getLowerValueLimosis();
                limitUp = upLowRecord.getUpperValueLimosis();
            } else if (entity.getSugarValueTimeTypeId() == SugarRecordType.EARLYMORNING.getType()) {
                //凌晨
                limitLow = upLowRecord.getLowerValueWeeHours();
                limitUp = upLowRecord.getUpperValueWeeHours();
            } else if (entity.getSugarValueTimeTypeId() == SugarRecordType.BEFORESLEEP.getType()) {
                // 睡前
                limitLow = upLowRecord.getLowerValueBeforeSleep();
                limitUp = upLowRecord.getUpperValueBeforeSleep();
            } else if (SugarRecordType.isAfterMeal(entity.getSugarValueTimeTypeId())) {
                //餐后
                limitLow = upLowRecord.getLowerValueAfterMeal();
                limitUp = upLowRecord.getUpperValueAfterMeal();
            } else {
                // 餐前
                limitLow = upLowRecord.getLowerValueBeforeMeal();
                limitUp = upLowRecord.getUpperValueBeforeMeal();
            }
            entity.setSugarValueType(SugarRecordUtil.judgeRecordStatus(entity.getSugarValueSugarValue(), limitLow, limitUp));
        }
        return super.save(entity);
    }

    private void handleRecordStatus(DtrdEntRdSigninSugarValue entity) {
        Integer recordType = Optional.ofNullable(entity.getSugarValueType()).orElse(0);
        if (recordType == 0) {
            ValueUplowBean upLowRecord = uplowRecordService.getSugarValueUpLowRecord(entity.getPatientId());
            log.info("SugarRecordServiceImpl.handleRecordStatus.[entity] upload=" + upLowRecord);
            int timeType = Optional.ofNullable(entity.getSugarValueTimeTypeId()).orElse(SugarRecordType.RANDOMTIME.getType());
            Integer recordStatus;
            if (timeType == SugarRecordType.EARLYMORNING.getType()) {
                recordStatus = judgeRecordStatus(entity.getSugarValueSugarValue(), upLowRecord.getLowerValueWeeHours(), upLowRecord.getUpperValueWeeHours());
            } else if (timeType == SugarRecordType.BEFOREBREAKFAST.getType()) {
                recordStatus = judgeRecordStatus(entity.getSugarValueSugarValue(), upLowRecord.getLowerValueLimosis(), upLowRecord.getUpperValueLimosis());
            } else if (timeType == SugarRecordType.BEFORESLEEP.getType()) {
                recordStatus = judgeRecordStatus(entity.getSugarValueSugarValue(), upLowRecord.getLowerValueBeforeSleep(), upLowRecord.getUpperValueBeforeSleep());
            } else if (SugarRecordType.isBeforeMeal(timeType)) {
                recordStatus = judgeRecordStatus(entity.getSugarValueSugarValue(), upLowRecord.getLowerValueBeforeMeal(), upLowRecord.getUpperValueBeforeMeal());
            } else {
                recordStatus = judgeRecordStatus(entity.getSugarValueSugarValue(), upLowRecord.getLowerValueLimosis(), upLowRecord.getUpperValueLimosis());
            }
            entity.setSugarValueType(recordStatus);
        }
    }

    private void noticeSugarRecordChange(DtrdEntRdSigninSugarValue entity) {
        // 补充血糖打卡
        Date recordDate = entity.getRecordDate();
        log.info("SugarRecordServiceImpl.noticeSugarRecordChange.[entity] after save patientId=" + entity.getPatientId() +"; dataId=" + entity.getDataId());
        signinRecordService.saveSigninRecord(entity.getDataId(), entity.getSugarValueTimeTypeId(), mapper ->
                mapper.apply(entity.getPatientId())
                        .withRecordType(RecordType.SUGAR.getType())
                        .withRecordValue(entity.getSugarValueSugarValue()+"")
                        .withRecordTime(DateUtils.formatDate(entity.getRecordTime(), DateUtils.datetimeFormat.get().toPattern()))
                        .build()
        );
//        signinRecordService.saveSigninRecord(entity.getDataId(), entity.getPatientId(),
//                Constant.RecordType.SUGAR.getType(),
//                entity.getSugarValueTimeTypeId(),
//                DateUtils.formatDate(recordDate, DateUtils.date_sdf.get().toPattern()));
//        if (Constant.SugarSourceType.isDeviceRecord(sourceType)) {
        // 设备上传记录
        doctorPatientService.onSugarRecord(entity.getPatientId(), entity.getDataId(), recordDate);
//        }
    }

    @Override
    public List<DtrdEntRdSigninSugarValue> getLatestSugarValue(String patientIds, int... timeTypes) {
        return baseMapper.getLatestSugarValue(patientIds, timeTypes);
    }

    @Override
    public void handleAbnormalSugarValue(CheckinRecord record) {
        float maxAbnormalSugarValue = 16.7F;
        float minAbnormalSugarValue = 3.9F;
        AbnormalSugarRecordBean abnormalSugarRecordBean = new AbnormalSugarRecordBean().init(record);
        AbnormalSugarRecordBean abnormalSugarRecordBeanForDoctor = new AbnormalSugarRecordBean().init(record);
        String message = "";

        Integer sugarValueType = SchemeUtil.compareSugarStatus(record.getSugarValue(), record.getSubType(), record.getPatientId());
        log.info("sugarValueType===" + sugarValueType);
        if (sugarValueType == Constant.RecordStatus.HIGHER.getType()) { // 偏高
            abnormalSugarRecordBeanForDoctor.setMessage("偏高");
            if (record.getSugarValue() > maxAbnormalSugarValue) { // 出现过严重高血糖（＞16.7mmol/L）
                message = moreThanMaxAbnormalSugarValue(record);
                abnormalSugarRecordBean.setMessage(message);
            } else if (checkTodaySugarValueOftenHigh(record)) { // 一天累积出现3次血糖偏高
                message = makeOftenHighMsg(record);
                abnormalSugarRecordBean.setMessage(message);
            } else if (continuousSugarValueHighMoreThanThreeDays(record)) { // 连续血糖偏高≥3天
                message = makeMoreThanThreeDaysHighMsg(record);
                abnormalSugarRecordBean.setMessage(message);
            } else if (sugarValueIsWaved(record)) { // 血糖波动（一周内至少出现3次高血糖或2次低血糖）
                message = makeSugarValueWavedMsg(record);
                abnormalSugarRecordBean.setMessage(message);
            } else { // 血糖偏高
                message = highAbnormalSugarValue(record);
                abnormalSugarRecordBean.setMessage(message);
            }
        } else if (sugarValueType == Constant.RecordStatus.LOWER.getType()) { // 偏低
            abnormalSugarRecordBeanForDoctor.setMessage("偏低");
            if (record.getSugarValue() <= minAbnormalSugarValue) { // 出现过严重低血糖（≤3.9mmol/L）
                log.info("=========================出现过严重低血糖（≤3.9mmol/L）==========");
                message = "您血糖偏低，低血糖对于脑细胞的损害较大，需要及时处理和严格预防，建议进食糖果类食物，并15分钟后复测血糖，如血糖仍未恢复至需要继续进食哦！";
                abnormalSugarRecordBean.setMessage(message);
            } else if (checkTodaySugarValueOftenLow(record)) { // 频繁出现低血糖（不同时段出现3次以上）
                message = "近期您频繁出现低血糖，低血糖对于脑细胞的损害较大，需要及时处理和严格预防，建议立即进食糖果类食物，并15分钟后复测血糖，如血糖仍未恢复则需要继续进食，同时请尽快就诊！";
                abnormalSugarRecordBean.setMessage(message);
            } else { // 低血糖
                message = "您血糖偏低，低血糖对于脑细胞的损害较大，需要及时处理和严格预防，建议进食糖果类食物，并15分钟后复测血糖，如血糖仍未恢复至需要继续进食哦！";
                abnormalSugarRecordBean.setMessage(message);
            }
        } else {
            message = "正常";
            abnormalSugarRecordBean.setMessage("近期你的血糖情况良好，血糖控制的越好，病情就越稳定，需要监测扎针的频次也就越低，继续保持！");
            abnormalSugarRecordBeanForDoctor.setMessage("正常");
        }

        log.info("====abnormalSugarRecordBean===" + abnormalSugarRecordBean);

        if (!message.equals("")) {
            abnormalSugarRecordBean.setWaveValue(computedWavedSugarValue(record));
            abnormalSugarRecordBeanForDoctor.setWaveValue(computedWavedSugarValue(record));
            msgEventPusher.sugarValueAbnormal(abnormalSugarRecordBean);
            msgEventPusher.sugarValueAbnormalForDoctor(abnormalSugarRecordBeanForDoctor);
        }
    }

    private String moreThanMaxAbnormalSugarValue(CheckinRecord record) {
        Constant.SugarRecordType typeId = Constant.SugarRecordType.valueOf(record.getSubType());
        String message = "";
        switch (typeId) {
            case BEFOREBREAKFAST:
                message = "空腹血糖高的原因有很多，通常和您前一天的晚餐或者体内激素分泌规律有关，建议您关注睡前、凌晨3点及次日的空腹血糖，帮助分析血糖异常的原因。且您出现过较严重的高血糖（＞16.7mmol/L），容易引发急性并发症， 需注意控制。";
                break;
            case AFTERBREAKFAST:
                message = "早餐后血糖偏高，建议您按照营养方案的饮食方案用餐，配合餐后1h的运动，近几天关注空腹血糖和餐后血糖情况，帮助分析血糖异常的原因。且您出现过较严重的高血糖（＞16.7mmol/L），容易引发急性并发症， 需注意控制。";
                break;
            case AFTERLUNCH:
                message = "午餐后血糖偏高，饮食搭配是否合理？建议您按照营养方案的饮食方案用餐，配合餐后1h的运动，近几天关注空腹血糖和餐后血糖情况，帮助分析血糖异常的原因。且您出现过较严重的高血糖（＞16.7mmol/L），容易引发急性并发症， 需注意控制。";
                break;
            case AFTERDINNER:
                message = "晚餐后血糖偏高，饮食搭配是否合理？建议您按照营养方案的饮食方案用餐，配合餐后1h的运动，近几天关注空腹血糖和餐后血糖情况，帮助分析血糖异常的原因。且您出现过较严重的高血糖（＞16.7mmol/L），容易引发急性并发症， 需注意控制。";
                break;
            default:
                message = "近期血糖值超过了16.7mmol/L，容易引发急性并发症。建议您仔细排查下原因同时增加血糖监测次数，针对性调整，必要时到医院就诊。";
                break;
        }
        return message;
    }

    private String highAbnormalSugarValue(CheckinRecord record) {
        Constant.SugarRecordType typeId = Constant.SugarRecordType.valueOf(record.getSubType());
        String message = "";
        switch (typeId) {
            case BEFOREBREAKFAST:
                message = "您空腹血糖偏高，空腹血糖高的原因有很多，通常和您前一天的晚餐或者体内激素分泌规律有关，建议您关注睡前、凌晨3点及次日的空腹血糖，以排除苏木杰现象及黎明现象，帮助分析血糖异常的原因。";
                break;
            case AFTERBREAKFAST:
                message = "您早餐后血糖偏高，是否早餐吃了过多高碳水食物呢？建议您按照营养方案用餐，配合适当的运动，要继续监测早餐后血糖，找到血糖异常的原因。";
                break;
            case AFTERLUNCH:
                message = "您午餐后血糖偏高，是否午餐吃了过多高碳水食物呢？建议您按照营养方案用餐，配合适当的运动，要继续监测午餐后血糖，找到血糖异常的原因。";
                break;
            case AFTERDINNER:
                message = "您晚餐后血糖偏高，是否晚餐吃了过多高碳水食物呢？建议您按照营养方案用餐，配合适当的运动，要继续监测晚餐后血糖，找到血糖异常的原因。";
                break;
            default:
                message = "";
                break;
        }
        return message;
    }

    private String makeOftenHighMsg(CheckinRecord record) {
        Constant.SugarRecordType typeId = Constant.SugarRecordType.valueOf(record.getSubType());
        String message = "";
        switch (typeId) {
            // case BEFOREBREAKFAST:
            //     message = "近期你出现一天内血糖多次偏高，某个时段血糖异常之后，若不注意控制，可能带来后续时段的血糖的连锁异常，且近期你的空腹血糖不太好，空腹血糖高的原因有很多，通常和你前一天的晚餐或者体内激素分泌规律有关，建议您监测睡前、凌晨3点及次日的空腹血糖，以排除苏木杰现象及黎明现象，帮助分析血糖异常的原因。";
            //     break;
            // case AFTERBREAKFAST:
            //     message = "近期你出现一天内血糖多次偏高，某个时段血糖异常之后，若不注意控制，可能带来后续时段的血糖的连锁异常，且近期你的早餐后血糖不太好，是否早餐吃了过于精细或者流质的主食呢？建议您按照管理处方的饮食方案用餐，配合适当的运动，另外第二天在空腹加测一次血糖，早餐后也要继续监测，帮助分析血糖异常的原因。";
            //     break;
            case AFTERLUNCH:
                message = "您一天内多次出现血糖偏高，且近期您的午餐后血糖偏高，是否午餐的搭配不太合理，主食进食过多？建议您按照营养方案用餐，配合适当运动，要继续监测血糖，找到血糖异常的原因。";
                break;
            case AFTERDINNER:
                message = "您一天内多次出现血糖偏高，且近期您的晚餐后血糖偏高，是否晚餐的搭配不太合理，主食进食过多？建议您按照营养方案用餐，配合适当运动，要继续监测血糖，找到血糖异常的原因。";
                break;
            case BEFOREDINNER:
            case BEFORESLEEP:
            case BEFOREBREAKFAST:
                message = "您一天内多次出现血糖偏高，若不注意控制，可能带来血糖的持续异常。建议尽快就诊，找到血糖异常的原因。";
                break;
            default:
                message = "";
                break;
        }
        return message;
    }

    private boolean checkTodaySugarValueOftenLow(CheckinRecord record) {
        int count = count(Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                .eq(DtrdEntRdSigninSugarValue::getRecordDate, record.getRecordDate())
                .eq(DtrdEntRdSigninSugarValue::getSugarValueType, Constant.RecordStatus.LOWER.getType()));
        return count >= 3;
    }

    private boolean checkTodaySugarValueOftenHigh(CheckinRecord record) {
        int count = count(Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                .eq(DtrdEntRdSigninSugarValue::getRecordDate, record.getRecordDate())
                .eq(DtrdEntRdSigninSugarValue::getSugarValueType, Constant.RecordStatus.HIGHER.getType()));
        return count >= 3;
    }

    private boolean continuousSugarValueHighMoreThanThreeDays(CheckinRecord record) {
        int yesterdayIsHigh = count(Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                .eq(DtrdEntRdSigninSugarValue::getRecordDate, DateUtils.dateAdd(-1, record.getRecordDate()))
                .eq(DtrdEntRdSigninSugarValue::getSugarValueType, Constant.RecordStatus.HIGHER.getType()));
        if (yesterdayIsHigh > 0) {
            int beforeYesterdayIsHigh = count(Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                    .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                    .eq(DtrdEntRdSigninSugarValue::getRecordDate, DateUtils.dateAdd(-2, record.getRecordDate()))
                    .eq(DtrdEntRdSigninSugarValue::getSugarValueType, Constant.RecordStatus.HIGHER.getType()));
            return beforeYesterdayIsHigh > 0;
        }
        return false;
    }

    private String makeMoreThanThreeDaysHighMsg(CheckinRecord record) {
        Constant.SugarRecordType typeId = Constant.SugarRecordType.valueOf(record.getSubType());
        String message = "";
        switch (typeId) {
            case BEFOREBREAKFAST:
                message = "近期您出现连续的空腹血糖偏高，空腹血糖高的原因有很多，通常和您前一天的晚餐或者体内激素分泌规律有关，建议您关注睡前、凌晨3点及次日的空腹血糖，以排除苏木杰现象及黎明现象，帮助分析血糖异常的原因。";
                break;
            case AFTERBREAKFAST:
                message = "近期您连续出现早餐后血糖偏高，是否早餐吃了过多高碳水食物呢？建议您按照营养方案用餐，配合适当的运动，要继续监测早餐后血糖，找到血糖异常的原因。";
                break;
            case AFTERLUNCH:
                message = "近期您连续出现午餐后血糖偏高，是否午餐吃了过多高碳水食物呢？建议您按照营养方案用餐，配合适当的运动，要继续监测午餐后血糖，找到血糖异常的原因。";
                break;
            case AFTERDINNER:
                message = "近期您连续出现晚餐后血糖偏高，是否晚餐吃了过多高碳水食物呢？建议您按照营养方案用餐，配合适当的运动，要继续监测晚餐后血糖，找到血糖异常的原因。";
                break;
            default:
                message = "近期您连续出现血糖偏高，可不能就此松懈，建议您先调整饮食、运动，保持良好生活习惯，若仍未改善请尽快就诊。";
                break;
        }
        return message;
    }

    private boolean sugarValueIsWaved(CheckinRecord record) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> highWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                .lt(DtrdEntRdSigninSugarValue::getRecordDate, record.getRecordDate())
                .gt(DtrdEntRdSigninSugarValue::getRecordDate, DateUtils.dateAdd(-7, record.getRecordDate()))
                .eq(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, record.getSubType())
                .ne(DtrdEntRdSigninSugarValue::getSugarValueType, Constant.RecordStatus.HIGHER.getType());
        if (count(highWrapper) > 3) {
            return true;
        }

        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> lowWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                .lt(DtrdEntRdSigninSugarValue::getRecordDate, record.getRecordDate())
                .gt(DtrdEntRdSigninSugarValue::getRecordDate, DateUtils.dateAdd(-7, record.getRecordDate()))
                .eq(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, record.getSubType())
                .ne(DtrdEntRdSigninSugarValue::getSugarValueType, Constant.RecordStatus.LOWER.getType());
        if (count(lowWrapper) > 2) {
            return true;
        }

        return false;
    }

    private String makeSugarValueWavedMsg(CheckinRecord record) {
        Constant.SugarRecordType typeId = Constant.SugarRecordType.valueOf(record.getSubType());
        String message = "你近期血糖波动较大，令人担心！请加强控制，必要时尽快就诊，让主治医生帮忙排查下存在的问题，尽快纠正当前的“失控”状态。";
        // switch (typeId) {
        //     case BEFOREBREAKFAST:
        //         message = "近期你的空腹血糖不太好，空腹血糖高的原因有很多，通常和你前一天的晚餐或者体内激素分泌规律有关，建议您监测睡前、凌晨3点及次日的空腹血糖，以排除苏木杰现象及黎明现象，帮助分析血糖异常的原因。且你近期血糖波动较大，令人担心！请加强控制，必要时尽快就诊，让主治医生帮忙排查下存在的问题，尽快纠正当前的“失控”状态。";
        //         break;
        //     case AFTERBREAKFAST:
        //         message = "近期你的早餐后血糖不太好，是否早餐吃了过于精细或者流质的主食呢？建议您按照管理处方的饮食方案用餐，配合适当的运动，另外第二天在空腹加测一次血糖，早餐后也要继续监测，帮助分析血糖异常的原因。且你近期血糖波动较大，令人担心！请加强控制，必要时尽快就诊，让主治医生帮忙排查下存在的问题，尽快纠正当前的“失控”状态。";
        //         break;
        //     case AFTERLUNCH:
        //         message = "近期你的午餐后血糖不太好，是否午餐的搭配不太合理，主食进食过多？建议您按照管理处方的饮食方案用餐，另外第二天在午餐前加测一次血糖，午餐后也要继续监测，帮助分析血糖异常的原因。且你近期血糖波动较大，令人担心！请加强控制，必要时尽快就诊，让主治医生帮忙排查下存在的问题，尽快纠正当前的“失控”状态。";
        //         break;
        //     case AFTERDINNER:
        //         message = "近期你的晚餐后血糖不太好，是不是餐后忘运动啦？建议您按照管理处方的饮食方案用餐，配合适当的运动，另外第二天在晚餐前加测一次血糖，晚餐后也要继续监测，帮助分析血糖异常的原因。且你近期血糖波动较大，令人担心！请加强控制，必要时尽快就诊，让主治医生帮忙排查下存在的问题，尽快纠正当前的“失控”状态。";
        //         break;
        //     default:
        //         message = "你近期血糖波动较大，令人担心！请加强控制，必要时尽快就诊，让主治医生帮忙排查下存在的问题，尽快纠正当前的“失控”状态。";
        //         break;
        // }
        return message;
    }

    /**
     * 血糖波动值=本次血糖值-上次血糖值
     * @param record
     * @return
     */
    private Float computedWavedSugarValue(CheckinRecord record) {
        LambdaQueryWrapper<DtrdEntRdSigninSugarValue> queryWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninSugarValue.class)
                .eq(DtrdEntRdSigninSugarValue::getPatientId, record.getPatientId())
                .eq(DtrdEntRdSigninSugarValue::getSugarValueTimeTypeId, record.getSubType())
                .eq(DtrdEntRdSigninSugarValue::getIsDel, 0)
                .orderByDesc(DtrdEntRdSigninSugarValue::getDataId)
                .last("limit 1,1");
        DtrdEntRdSigninSugarValue dtrdEntRdSigninSugarValue = getOne(queryWrapper);
        log.info("dtrdEntRdSigninSugarValue========" + dtrdEntRdSigninSugarValue);
        if (dtrdEntRdSigninSugarValue != null) {
            float wave = Math.abs(dtrdEntRdSigninSugarValue.getSugarValueSugarValue() - record.getSugarValue());
            return StringUtil.StrTrimFloat(String.format("%.1f", wave));
        } else {
            return 0F;
        }
    }
}
